use deno_core::{error::AnyError, include_js_files, op, v8::*, Extension};
use uuid::Uuid;

use crate::{
    function::scene::{components::TransformComponent, scene::SceneServer},
    input::INPUT,
    math::Vector3f,
};

pub struct JsBinding;

/// Extension
impl JsBinding {
    pub fn get_extension() -> Extension {
        Extension::builder()
            .js(include_js_files!(
                prefix "alice:2d",
                "preload/alice.js",
            ))
            .ops(vec![
                op_is_key_pressed::decl(),
                op_get_position::decl(),
                op_set_position::decl(),
            ])
            .build()
    }
}

/// Ops
#[op]
fn op_is_key_pressed(code: i32) -> Result<bool, AnyError> {
    if let Some(input) = unsafe { INPUT.get() } {
        Ok(input.is_key_pressed(code.into()))
    } else {
        Ok(false)
    }
}

/// Ops
#[op]
fn op_get_position(id: Uuid) -> Result<Vector3f, AnyError> {
    let server = SceneServer::get_server();
    let runtime_scene = server.get_active();
    let transform = runtime_scene
        .get_component::<TransformComponent>(id)
        .unwrap();

    Ok(transform.translation)
}

#[op]
fn op_set_position(id: Uuid,pos:Vector3f) -> Result<(), AnyError> {
    let server = SceneServer::get_server_mut();
    let runtime_scene = server.get_active_scene_mut();

    let entity = runtime_scene.uid_entity_map.get(&id).unwrap();
    let mut transform = runtime_scene.world.get_mut::<TransformComponent>(entity.handle).unwrap();

    transform.translation = pos;
    Ok(())
}

/// Function
impl JsBinding {
    pub fn try_invoke_js_function(
        scope: &mut HandleScope,
        holder: Local<Value>,
        name: &str,
        args: &[Local<Value>],
    ) {
        if holder.is_object() {
            let obj = holder.to_object(scope).unwrap();
            let func_name = String::new(scope, name).unwrap();
            let func = obj.get(scope, func_name.into()).unwrap();
            if func.is_function() {
                let func = Local::<Function>::try_from(func)
                    .expect(&format!("获取脚本 {} 方法失败", name));

                func.call(scope, obj.into(), args);
            }
        }
    }
}
